﻿using System;
using System.Collections.Generic;
using System.Drawing.Design;
using System.Linq;
using System.Windows.Forms.Design;
using devtm.Editor.TypeSelector;
using DslModeling = global::Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling;
using System.Windows.Forms;
using System.Reflection;
using System.Windows.Data;
using Verbatim.CustomConverters;
using System.ComponentModel;


namespace devtm.AutoMapper
{

    public partial class ListIValueConverterEditor : UITypeEditor
    {

        protected DslModeling.Store Store;
        protected ItemMap Instance;

        /// <summary>
        /// Edits the specified object's value using the editor style indicated by the <see cref="M:System.Drawing.Design.UITypeEditor.GetEditStyle"/> method.
        /// </summary>
        /// <param name="context">An <see cref="T:System.ComponentModel.ITypeDescriptorContext"/> that can be used to gain additional context information.</param>
        /// <param name="provider">An <see cref="T:System.IServiceProvider"/> that this editor can use to obtain services.</param>
        /// <param name="value">The object to edit.</param>
        /// <returns>
        /// The new value of the object. If the value of the object has not changed, this should return the same object it was passed.
        /// </returns>
        public override object EditValue(System.ComponentModel.ITypeDescriptorContext context, IServiceProvider provider, object value)
        {

            IWindowsFormsEditorService edSvc = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
            if (edSvc != null)
            {

                if (context.Instance.GetType().IsArray)
                    Instance = ((Object[])context.Instance)[0] as ItemMap;
                else
                    Instance = context.Instance as ItemMap;

                Store = Instance.Store;

                using (var repository = CustomTypeRepository.Instance(this.Store, true))
                {

                    Mapper mapper = GetMapper(context);
                    TypeElement typeElement = GetTypeElement(context, mapper);

                    ListBox control = new ListBox();
                    control.DisplayMember = "Name";
                    control.ValueMember = "AssemblyQualifiedName";

                    //HashSet<string> namespaces = typeElement.MappingModel.GetNamespaces;

                    List<Mono.Cecil.TypeReference> lst = null;

                    if (Instance.TypeCast == CastType.IValueConverter)
                        lst = LoadTypes<IValueConverter>(typeElement, repository).ToList();

                    else if (Instance.TypeCast == CastType.IConverter)
                        lst = LoadTypes<IConverter>(typeElement, repository).ToList();

                    else if (Instance.TypeCast == CastType.TypeConverter)
                        lst = LoadTypes<TypeConverter>(typeElement, repository).ToList();


                    control.DataSource = lst.OrderBy(c => c.Name).ToList();

                    control.SelectedValue = lst.FirstOrDefault(c => c.AssemblyQualifiedName == (string)value);

                    if (!String.IsNullOrEmpty((string)value))
                        control.SelectedValue = value;

                    var d = DateTime.Now;


                   

                    control.SelectedValueChanged +=
                        (sender, e) =>
                        {

                            if (DateTime.Now.Subtract(d).Seconds < 1)
                                return;

                            try
                            {
                                value = (control.SelectedItem as Mono.Cecil.TypeReference).AssemblyQualifiedName;
                            }
                            catch (Exception ex)
                            {
                                Helpers._output.WriteLine(ex.ToString());
                            }

                        };

                    edSvc.DropDownControl(control);

                }

            }

            return value;

        }

        private IEnumerable<Mono.Cecil.TypeReference> LoadTypes<T>(TypeElement typeElement, CustomTypeRepository repository)
        {
            Predicate<Mono.Cecil.TypeDefinition> predicate = type => { return type.InheritFrom(typeof(T).FullName); };
            var list = repository.GetTypes(predicate ).ToList();
            return list;
        }


        private static Mapper GetMapper(System.ComponentModel.ITypeDescriptorContext context)
        {

            ItemMap p1;

            if (context.Instance.GetType().IsArray)
                p1 = ((Object[])context.Instance)[0] as ItemMap;
            else
                p1 = context.Instance as ItemMap;

            return p1.Mapper;
        }


        public override bool IsDropDownResizable
        {
            get
            {
                if (Instance != null && ShowDropDown(Instance))
                    return true;

                return false;
            }
        }

        public override UITypeEditorEditStyle GetEditStyle(System.ComponentModel.ITypeDescriptorContext context)
        {

            if (context != null && context.Instance != null)
            {
                if (context.Instance.GetType().IsArray)
                    Instance = ((Object[])context.Instance)[0] as ItemMap;
                else
                    Instance = context.Instance as ItemMap;

            }
            if (Instance != null && ShowDropDown(Instance))
                return UITypeEditorEditStyle.DropDown;

            return UITypeEditorEditStyle.None;
            //return base.GetEditStyle(context);
        }


        private bool ShowDropDown(ItemMap instance)
        {
            switch (instance.TypeCast)
            {
                case CastType.Implicit:
                case CastType.Custom:
                case CastType.Map:
                case CastType.None:
                case CastType.Explicit:
                case CastType.Collection:
                case CastType.List:
                case CastType.ObservableCollection:
                    return false;

                case CastType.IConverter:
                case CastType.IValueConverter:
                default:
                    return true;
            }
        }

            
        private static TypeElement GetTypeElement(System.ComponentModel.ITypeDescriptorContext context, Mapper mapper)
        {
            if (context.PropertyDescriptor.Name.Contains("Source"))
                return mapper.MappingSource.TypeElement;

            return mapper.MappingDestination.TypeElement;

        }


    }

}